using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Net;
using System.IO;


namespace Devsoft.Utilities
{
    public class DownloadClient
    {
        #region [ Events ]

        public event EventHandler Complete;
        public event EventHandler Cancelled;
        public event EventHandler<ProgressChangedEventArgs> ProgressChanged;
        public class ProgressChangedEventArgs : EventArgs
        {
            #region [ Properties ]

            private Int32 _Progress;
            public Int32 Progress
            {
                get { return _Progress; }
                set { _Progress = value; }
            }

            #endregion


            #region [ Constructor ]

            public ProgressChangedEventArgs(Int32 progress)
            {
                this.Progress = progress;
            }

            #endregion
        }

        #endregion


        #region [ Field ]

        private BackgroundWorker _backgroundWorker = null;

        #endregion


        #region [ Constructor ]

        public DownloadClient() { }

        #endregion


        #region [ Methods ]

        public void Start(String url, String path, Int32 startPoint)
        {
            List<Object> arguments = new List<Object>();
            arguments.Add(url);
            arguments.Add(path);
            arguments.Add(startPoint);
            _backgroundWorker = CreateWorker();
            _backgroundWorker.RunWorkerAsync(arguments);
        }

        public void Cancel()
        {
            _backgroundWorker.CancelAsync();
        }

        private void DoWork(Object sender, DoWorkEventArgs e)
        {
            List<Object> arguments = (List<Object>)e.Argument;
            HttpWebRequest req = null;
            HttpWebResponse res = null;
            Stream resStream = null;
            Stream localStream = null;

            try
            {
                req = (HttpWebRequest)WebRequest.Create((String)arguments[0]);
                req.ContentType = "Application";
                req.AddRange((Int32)arguments[1]);
                req.Credentials = CredentialCache.DefaultCredentials;
                res = (HttpWebResponse)req.GetResponse();

                Int64 fileSize = res.ContentLength;

                resStream = res.GetResponseStream();

                if ((Int32)arguments[2] == 0)
                    localStream = new FileStream((String)arguments[1], FileMode.Create, FileAccess.Write, FileShare.None);
                else
                    localStream = new FileStream((String)arguments[1], FileMode.Append, FileAccess.Write, FileShare.None);

                int bytesSize = 0;

                byte[] downBuffer = new byte[2048];

                while ((bytesSize = resStream.Read(downBuffer, 0, downBuffer.Length)) > 0)
                {
                    localStream.Write(downBuffer, 0, bytesSize);

                    _backgroundWorker.ReportProgress(Convert.ToInt32((localStream.Length * 100) / fileSize + (Int32)arguments[2]));

                    if (_backgroundWorker.CancellationPending)
                    {
                        e.Cancel = true;
                        break;
                    }
                }
            }
            catch { throw; }
            finally
            {
                if (null != req)
                    req = null;
                if (null != res)
                    res.Close();
                if (null != resStream)
                    resStream.Close();
                if (null != localStream)
                    localStream.Close();
            }
        }

        private void WorkerProgressChanged(Object sender, System.ComponentModel.ProgressChangedEventArgs e)
        {
            OnProgressChanged(e.ProgressPercentage);
        }

        private void WorkerCompleted(Object sender, RunWorkerCompletedEventArgs e)
        {
            if (null != e.Error)
                throw new Exception("Operation cancelled!", e.Error);
            if (e.Cancelled)
                OnCancelled();
            else
                OnComplete();
        }

        #endregion


        #region [ Helper Methods ]

        private void OnProgressChanged(Int32 progress)
        {
            EventHandler<ProgressChangedEventArgs> copy = ProgressChanged;
            if (null != copy)
                copy(this, new ProgressChangedEventArgs(progress));
        }

        private void OnComplete()
        {
            EventHandler copy = Complete;
            if (null != copy)
                copy(this, EventArgs.Empty);
        }

        private void OnCancelled()
        {
            EventHandler copy = Cancelled;
            if (null != copy)
                copy(this, EventArgs.Empty);
        }

        public BackgroundWorker CreateWorker()
        {
            BackgroundWorker bg = new BackgroundWorker();
            bg.WorkerReportsProgress = true;
            bg.WorkerSupportsCancellation = true;
            bg.DoWork += DoWork;
            bg.ProgressChanged += WorkerProgressChanged;
            bg.RunWorkerCompleted += WorkerCompleted;
            return bg;
        }

        #endregion
    }
}
